#ifndef __DRIVER_SERIAL_H
#define __DRIVER_SERIAL_H

#include <lib/type.h>
#include <os/driver.h>

#define DRIVER_NAME "uart-serial"
#define DEVICE_NAME "com"
#define DRIVER_VERSION "0.1"

// com io address
#define COM1_BASE 0x3F8
#define COM2_BASE 0x2F8
#define COM3_BASE 0x3E8
#define COM4_BASE 0x2E8

// max baud value
#define BAUD_VALUE_MAX 115200

#define DEFAULT_BAUD_VALUE 19200
#define DEFAULT_DIVISION_VALUE (BAUD_VALUE_MAX / DEFAULT_BAUD_VALUE)

// max number of serials
#define COM_NUM_MAX 4

// com number
enum com
{
    COM1 = 0x0,
    COM2 = 0x2,
    COM3 = 0x3,
    COM4 = 0x04,
};

// baud number
enum baud
{
    BAUD_50 = 0x0,
    BAUD_110,
    BAUD_220,
    BAUD_300,
    BAUD_600,
    BAUD_1200,
    BAUD_2400,
    BAUD_4800,
    BAUD_9600,
    BAUD_19200,
    BAUD_38400,
    BAUD_57600,
    BAUD_115200,
    BAUD_MAX_NUM
};

enum word
{
    WORD_LEN_5 = 0x0,
    WORD_LEN_6,
    WORD_LEN_7,
    WORD_LEN_8,
    WORD_MAX_NUM
};

enum stop
{
    STOP_1 = 0x0,
    STOP_2,
    STOP_MAX_NUM
};

enum parity
{
    PARITY_NO = 0x0,
    PARITY_ODD,
    PARITY_EVEN,
    PARITY_MASK,
    PARITY_SPACE,
    PARITY_MAX_NUM
};

enum line_ctrl_reg
{
    // word lenght
    LINE_WORD_LEN_5 = (0 << 0),                           // word lenght 5bits
    LINE_WORD_LEN_6 = (0 << 1),                           // word lenght 6bits
    LINE_WORD_LEN_7 = (1 << 1),                           // word lenght 7bits
    LINE_WORD_LEN_8 = ((1 << 1) | (1 << 0)),              // word length 8bits
    LINE_STOP_BIT_1 = (0 << 2),                           // stop bit 1bits
    LINE_STOP_BIT_2 = (1 << 2),                           // stop bit 1.5bits or 2b
                                                          // parity select
    LINE_PARITY_NO = (0 << 3),                            // No parity
    LINE_PARITY_ODD = (1 << 3),                           // Odd parity
    LINE_PARITY_EVEN = ((1 << 3) | (1 << 4)),             // Even parity
    LINE_PARITY_MARK = ((1 << 3) | (1 << 5)),             // Mark
    LINE_PARITY_SPACE = ((1 << 3) | (1 << 4) | (1 << 5)), // Space
    LINE_BREAK_ENABLE = (1 << 6),                         // set break enable
    LINE_DLAB = (1 << 7),                                 // divisor latch access bit
};

enum int_enable_reg
{
    INT_RECV_DATA_AVILABLE = (1 << 0), // enable receive data avilable interrupt
    INT_TRANSMIT_HOLDING = (1 << 1),   // enable transmitter holding register empty interrupt
    INT_RECV_LINE_STATUS = (1 << 2),   // enable receiver line status interrupt
    INT_MODEM_STATUS = (1 << 3),       // enable modem status interrupt
    INT_SLEEP_MODE = (1 << 4),         // enable sleep mode
    INT_LOW_POWER_MODE = (1 << 5),     // enable low power mode
    INT_RESERVED1 = (1 << 6),          // reserved
    INT_RESERVED2 = (1 << 7),          // reserved
};

enum line_status_reg
{
    LINE_STATUS_DATA_READY = (1 << 0),                // data ready
    LINE_STATUS_OVERRUN_ERROR = (1 << 1),             // overrun error
    LINE_STATUS_PARITY_ERROR = (1 << 2),              // parity error
    LINE_STATUS_FRAMING_ERROR = (1 << 3),             // framing error
    LINE_STATUS_BREAK_INTERRUPT = (1 << 4),           // break interrupt
    LINE_STATUS_EMPTY_TRANSMITTER_HOLDING = (1 << 5), // empty transmitter holding register
    LINE_STATUS_EMPTY_DARA_HOLDING = (1 << 6),        // empty data holding registers
    LINE_STATUS_ERROR_RECEIVE_FIFO = (1 << 7),        // error in receive FIFO
};

enum fifo_control_reg
{
    FIFO_ENABLE = (1 << 0),                // enable fifo
    FIFO_CLEAR_RECEIVE = (1 << 1),         // clear receive fifo
    FIFO_CLEAR_TRANSMIT = (1 << 2),        // clear transmit fifo
    FIFO_DMA_MODE_SLECT = (1 << 3),        // DMA mode select
    FIFO_RESERVED = (1 << 4),              // reserved
    FIFO_ENABLE_64 = (1 << 5),             // enable 64 bytes fifo
    FIFO_TRIGGER_1 = (0 << 6),             // 1 byte
    FIFO_TRIGGER_4 = (1 << 6),             // 4 byte
    FIFO_TRIGGER_8 = (1 << 7),             // 8 byte
    FIFO_TRIGGER_14 = (1 << 6) | (1 << 7), // 14 byte
};

enum modem_ctrl_reg
{
    MOMED_DATA_TERMINAL_READY = (1 << 0),
    MOMED_REQUEST_TO_SEND = (1 << 1),
    MOMED_AUXILIARY_OUTPUT_1 = (1 << 2),
    MOMED_AUXILIARY_OUTPUT_2 = (1 << 3),
    MOMED_LOOKBACK_MODE = (1 << 4),
    MOMED_RESERVED0 = (1 << 5),
    MOMED_RESERVED1 = (1 << 6),
    MOMED_RESERVED2 = (1 << 7)
};

enum int_indenty_regBits
{
    INT_STATUS_PENDING_FLAG = 1,                       /* Interrupt Pending Flag */
    INT_STATUS_MODEM = (1 << 0),                       /* Transmitter Holding Register Empty Interrupt	 */
    INT_STATUS_TRANSMITTER_HOLDING = (1 << 1),         /* Received Data Available Interrupt */
    INT_STATUS_RECEIVE_DATA = (1 << 2),                /* Received Data Available Interrupt */
    INR_STATUS_RECEIVE_LINE = (1 << 1) | (1 << 2),     /* Receiver Line Status Interrupt */
    INT_STATUS_TIME_OUT_PENDING = (1 << 2) | (1 << 3), /* Time-out Interrupt Pending (16550 & later) */
    INT_STATUS_64BYTE_FIFO = (1 << 5),                 /* 64 Byte FIFO Enabled (16750 only) */
    INT_STATUS_NO_FIFO = (0 << 6),                     /* No FIFO on chip */
    INT_STATUS_RESERVED_CONDITION = (1 << 6),          /* Reserved condition */
    INT_STATUS_FIFO_NOT_FUNC = (1 << 7),               /* FIFO enabled, but not functioning */
    INT_STATUS_FIFO = (1 << 6) | (1 << 7),             /* FIFO enabled */
};

typedef struct
{
    uint8_t irq;

    char device_name[DEVICE_NAME_LEN+1]; // device name
    // serial register
    uint16_t iobase;           // io base
    uint16_t data_reg;         // data register
    uint16_t divisor_low_reg;  // divisor low register
    uint16_t int_enable_reg;   // interrupt enable register
    uint16_t divisor_high_reg; // divisor high register
    uint16_t int_identify_reg; // interrupt identify register
    uint16_t fifo_reg;         // fifo register
    uint16_t line_ctrl_reg;    // line control register
    uint16_t modem_ctrl_reg;   // modem control register
    uint16_t modem_status_reg; // modem status register
    uint16_t line_status_reg;  // line status register
    uint16_t scratch_reg;      // scratch register

    uint8_t baud;   // baud
    uint8_t word;   // word lenght
    uint8_t stop;   // stop bits
    uint8_t parity; // parity
} device_extension_t;

#endif